﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Collections.ObjectModel;
using yaTDL.Extensions;
using yaTDL.Core.IO;
using System.Windows.Input;

namespace yaTDL.Core.MVVM
{
    public class SearchViewModel : ViewModelBase
    {
        #region Fields

        XMLFileHandler xmlFileHandler = new XMLFileHandler(false);

        string _searchTerm;

        SortableObservableCollection<ObjectWithId> _results = new SortableObservableCollection<ObjectWithId>();

        ObservableCollection<SavedSearch> _savedSearches = new ObservableCollection<SavedSearch>();

        Visibility _viewVisibility = Visibility.Hidden;

        SavedSearch _selectedSavedSearch;

        RelayCommand _showItemCommand;
        RelayCommand _saveSearchCommand;
        RelayCommand _deleteSelectedSavedSearchCommand;

        #endregion


        #region Properties

        public string SearchTerm
        {
            get
            { 
                return _searchTerm; 
            }
            set
            {
                if (value != _searchTerm)
                {
                    _searchTerm = value;
                    RaisePropertyChanged("SearchTerm");                    
                }
            }
        }

        public SortableObservableCollection<ObjectWithId> Results
        {
            get
            { 
                return _results; 
            }
            set
            {
                if (value != _results)
                {
                    _results = value;
                    RaisePropertyChanged("Results");
                }
            }
        }

        public ObservableCollection<SavedSearch> SavedSearches
        {
            get { return _savedSearches; }
            set
            {
                if (value != _savedSearches)
                {
                    _savedSearches = value;
                    RaisePropertyChanged("SavedSearches");
                }
            }
        }

        public Visibility ViewVisibility
        {
            get
            {
                return _viewVisibility;
            }
            set
            {
                if (value != _viewVisibility)
                {
                    _viewVisibility = value;
                    RaisePropertyChanged("ViewVisibility");
                }
            }
        }

        public SavedSearch SelectedSavedSearch
        {
            get { return _selectedSavedSearch; }
            set 
            {
                if (value != _selectedSavedSearch)
                {
                    _selectedSavedSearch = value;
                    RaisePropertyChanged("SelectedSavedSearch");
                }
            }
        }

        public ICommand ShowItemCommand
        {
            get 
            {
                if (_showItemCommand == null)
                {
                    _showItemCommand = new RelayCommand(p => this.showItemCommandExecute(p));
                }
                return _showItemCommand;
            }
        }

        public ICommand SaveSearchCommand
        {
            get
            {
                if (_saveSearchCommand == null)
                {
                    _saveSearchCommand = new RelayCommand(p => this.saveSearchCommandExecute(p), p => this.saveSearchCommandCanExecute(p));
                }
                return _saveSearchCommand;
            }
        }

        public ICommand DeleteSelectedSavedSearchCommand
        {
            get
            {
                if (_deleteSelectedSavedSearchCommand == null)
                {
                    _deleteSelectedSavedSearchCommand = new RelayCommand(p => this.deleteSelectedSavedSearchCommandExecute(p), p => this.deleteSelectedSavedSearchCommandCanExecute(p));
                }
                return _deleteSelectedSavedSearchCommand;
            }
        }

        #endregion


        public SearchViewModel()
        {
            this.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(SearchViewModel_PropertyChanged);

            TaskStore.TaskPropertyChanged += new EventHandler<System.ComponentModel.PropertyChangedEventArgs>(TaskStore_TaskPropertyChanged);            

            SavedSearches = new ObservableCollection<SavedSearch>(xmlFileHandler.GetSavedSearches());
            SavedSearches.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(SavedSearches_CollectionChanged);

        }

        void SavedSearches_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            xmlFileHandler.SaveSavedSearches(SavedSearches);
        }

        void TaskStore_TaskPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            //update the search-results if any Task's property has changed, as it could be relevant for search.
            search();
        }

        void SearchViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "SearchTerm")
            {
                if (String.IsNullOrEmpty(SearchTerm))
                    Results = new SortableObservableCollection<ObjectWithId>();
                else
                    search();
                
                ViewVisibility = String.IsNullOrEmpty(SearchTerm) ? Visibility.Hidden : Visibility.Visible;
                SelectedSavedSearch = null;
                
            }
            else if(e.PropertyName == "SelectedSavedSearch")
            {
                if (SelectedSavedSearch != null)
                {
                    Results = new SortableObservableCollection<ObjectWithId>(search(parseSearchTerm(SelectedSavedSearch.SearchTerm)));
                }
                else
                {
                    if (String.IsNullOrEmpty(SearchTerm))
                        Results = new SortableObservableCollection<ObjectWithId>();
                }
            }
            else if (e.PropertyName == "SavedSearches")            
            {
                xmlFileHandler.SaveSavedSearches(SavedSearches);
                SavedSearches.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(SavedSearches_CollectionChanged);
            }
        }

        private void search()
        {
            Results = new SortableObservableCollection<ObjectWithId>(search(parseSearchTerm(SearchTerm))); 
        }
       

        static List<string> getSearchParams(string inputString)
        {

            if (inputString == null)
            {
                return null;
            }

            List<string> cmds = inputString.Split(' ').ToList<string>();

            int maxIndex = cmds.Count;

            for (int i = 0; i < maxIndex; i++)
            {
                if (cmds[i].StartsWith("\""))
                {

                    cmds[i] = cmds[i].Remove(0, 1);

                    bool foundEnd = false;

                    while (!foundEnd)
                    {
                        cmds[i] += " " + cmds[i + 1];

                        if (cmds[i + 1].EndsWith("\""))
                        {
                            foundEnd = true;

                            cmds[i] = cmds[i].Remove(cmds[i].Length - 1, 1);
                        }

                        cmds.RemoveAt(i + 1);
                        maxIndex -= 1;
                    }
                }
            }

            return cmds;
        }

        static SearchQuery parseSearchTerm(string searchTerm)
        {

            if (searchTerm == null)
            {
                return null;
            }


            SearchQuery result = new SearchQuery();

            List<string> _params = getSearchParams(searchTerm);

            


            var emptyModifiers = (from param in _params
                                  where param.ToLower() == "type:" ||
                                  isFragment(param.ToLower().Replace("type:", ""), "folder", "project", "task") ||
                                  param.ToLower() == "priority:" ||
                                  param.ToLower() == "due:" ||
                                  isFragment(param.ToLower().Replace("due:", ""), "yesterday", "tomorrow", "today")||
                                  param.ToLower() == "completed:" ||
                                  isFragment(param.ToLower().Replace("completed", ""), "true" , "false", "yes", "no")
                                  select param).ToList<string>();

            foreach (string s in emptyModifiers)
            {
                _params.Remove(s);
            }            



            if(_params.Any(x=> x.ToLower().StartsWith("type:")))
            {
                result.IncludeProjects = false;
                result.IncludeTasks = false;
            }

            if (_params.Any(x => x.ToLower().StartsWith("completed:")))
            {
                result.IncludePendingTasks = false;
                result.IncludeCompletedTasks = false;
            }


            foreach (string s in _params)
            {
                if (s.ToLower().StartsWith("type:"))
                {                               
                    switch (s.ToLower().Replace("type:", ""))
                    {
                        case "folder":
                        case "project":
                            result.IncludeProjects = true;
                            break;
                        case "task":
                            result.IncludeTasks = true;
                            break;
                    }
                }
                else if (s.ToLower().StartsWith("priority:"))
                {
                    int i;
                    if (int.TryParse(s.Replace("priority:", ""), out i))
                    {
                        result.Priorities.Add(i);
                    }
                }
                else if (s.ToLower().StartsWith("due:"))
                {
                    DateTime d;
                    string date = s.ToLower().Replace("due:", "");
                    if (DateTime.TryParse(date, out d))
                    {
                        result.DueDates.Add(d);
                    }
                    else
                    {
                        if (date == "today")
                            result.DueDates.Add(DateTime.Now.Date);
                        if (date == "tomorrow")
                            result.DueDates.Add(DateTime.Now.AddDays(1).Date);
                        if (date == "yesterday")
                            result.DueDates.Add(DateTime.Now.AddDays(-1).Date);
                    }
                }
                else if (s.ToLower().StartsWith("completed:"))
                {
                    switch (s.ToLower().Replace("completed:", ""))
                    {
                        case "true":
                        case "yes":
                            result.IncludeCompletedTasks = true;
                            break;
                        case "false":
                        case "no":
                            result.IncludePendingTasks = true;
                            break;
                    }
                }
                else
                {
                    result.Keywords.Add(s);
                }
                
            }


            
            if (!result.Keywords.Any())
                result.Keywords.Add("");
            else
            {
                if (result.Keywords.Count > 1)
                {
                    if (result.Keywords.Any(x => x == ""))
                    {
                        result.Keywords.Remove("");
                    }
                }

            }
            

            return result;
        }

        /// <summary>
        /// Checks if the specified string is a substring of one of the indicated strings.
        /// </summary>
        /// <param name="possibleFragment">The String suspected to be a substring.</param>
        /// <param name="possibleFullStrings">The Strings whichs substrings is to be checked for a match.</param>
        /// <returns>Returns true if possibleFragment is a substring of any of the possible "full" strings.</returns>
        private static bool isFragment(string possibleFragment, params string[] possibleFullStrings)
        {
            foreach (string s in possibleFullStrings)
            {
                if (s.Length > possibleFragment.Length)
                {
                    if (possibleFragment == s.Substring(0, possibleFragment.Length) && !String.IsNullOrEmpty(possibleFragment) && possibleFragment != s)
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        static IEnumerable<ObjectWithId> search(SearchQuery query)
        {

            if (query == null)
            {
                return new List<ObjectWithId>();
            }

            
            Dictionary<ObjectWithId, int> result = new Dictionary<ObjectWithId, int>();

            

            if (query.IncludeProjects) 
            {                              

                foreach (string s in query.Keywords)
                {
                    var pquery = TaskStore.GetProjects().Where(p => p.Name.ToLower().Contains(s.ToLower()));

                    foreach (Project p in pquery)
                    {
                        if (result.ContainsKey(p))
                        {
                            result[p] += 1;

                            if (p.Name.Contains(s))
                            {
                                result[p] += 1;
                            }
                        }
                        else
                        {

                            result.Add(p, 1);
                        }                        
                    }
                }                
            }


            if (query.IncludeTasks)
            {

                foreach (string s in query.Keywords)
                {
                    

                    List<Task> taskResults = TaskStore.GetTasks().Where(t => t.Content.ToLower().Contains(s.ToLower())).ToList<Task>();

                    if (!query.IncludeCompletedTasks) 
                    {
                        var tQuery = taskResults.Where(x => !x.Completed);
                        taskResults = tQuery.ToList<Task>();
                    }

                    if (!query.IncludePendingTasks)
                    {
                        var tQuery = taskResults.Where(x => x.Completed);
                        taskResults = tQuery.ToList<Task>();
                    }


                    if (query.DueDates.Any())
                    {
                        var tQuery = taskResults.Where(x => query.DueDates.Any(y=> y.Date == x.DueDate.Date));
                        taskResults = tQuery.ToList<Task>();
                    }

                    if (query.Priorities.Any())
                    {
                        var tQuery = taskResults.Where(x => query.Priorities.Contains(x.Priority));
                        taskResults = tQuery.ToList<Task>();
                    }


                    foreach (Task t in taskResults)
                    {
                        if (result.ContainsKey(t))
                        {
                            result[t] += 1;

                            if (t.Content.Contains(s))
                            {
                                result[t] += 1;
                            }
                        }
                        else
                        {                           
                            result.Add(t,1);
                        }                       
                    }
                }            
            }


            return result.Keys.ToList<ObjectWithId>().OrderByDescending<ObjectWithId, int>(x => result[x]);
        }

        



        #region Command Methods

        private void showItemCommandExecute(object _parameter)
        {
            if (_parameter is Project)
            {
                ProjectListViewModel vm = new ProjectListViewModel();
                vm.SelectedProject = (Project)_parameter;
                ViewManager.ShowView(vm);
            }
            else if (_parameter is Task)
            {
                ProjectListViewModel vm = new ProjectListViewModel();

                if (vm.Projects.Any())
                {

                    vm.SelectedProject = vm.Projects.First(x => x.Id == ((Task)_parameter).ProjectId);

                    vm.TaskListViewModel.SelectedTask = (Task)_parameter;
                    ViewManager.ShowView(vm);
                }
            }
            else
            {
                yaTDL.Core.UI.Dialogs.ShowMessageBox("Seems not to work yet");
            }
        }

        private bool saveSearchCommandCanExecute(object parameter = null)
        {
            return !String.IsNullOrEmpty(SearchTerm);
        }

        private void saveSearchCommandExecute(object parameter = null)
        {
            SavedSearch savedSearch = new SavedSearch();

            savedSearch.SearchTerm = SearchTerm;

            InputBoxViewModel inputBoxVM = new InputBoxViewModel();
            inputBoxVM.Text = SearchTerm;
            inputBoxVM.Title = "name your search";
            ViewManager.ShowView(inputBoxVM);

            if (!inputBoxVM.WasCanceled)
            {
                savedSearch.Name = inputBoxVM.Text;
                SavedSearches.Add(savedSearch);                
                SelectedSavedSearch = savedSearch;
            }

        }

        private bool deleteSelectedSavedSearchCommandCanExecute(object parameter = null)
        {
            return SelectedSavedSearch == null ? false : true;
        }

        private void deleteSelectedSavedSearchCommandExecute(object parameter = null)
        {
            if (SelectedSavedSearch != null)
            {
                SavedSearches.Remove(SelectedSavedSearch);
                if(SavedSearches.Any())
                    SelectedSavedSearch = SavedSearches.First();
            }
        }

        #endregion


    }

    class SearchQuery
    {
        public SearchQuery()
        {
            Keywords = new List<string>();
            IncludeProjects = true;
            IncludeTasks = true;
            IncludePendingTasks = true;
            IncludeCompletedTasks = true;
            Priorities = new List<int>();
            DueDates = new List<DateTime>();
        }

        public bool IncludeProjects { get; set; }
        public bool IncludeTasks { get; set; }

        public bool IncludePendingTasks { get; set; }
        public bool IncludeCompletedTasks { get; set; }

        public List<DateTime> DueDates { get; set; }
        public List<int> Priorities { get; set; }

        public List<string> Keywords { get; set; }
    }


}
